package com.share.common.utils;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * @author 01401061 - 孙艳强
 * @date 2020/10/30 18:56
 */
public class DateUtil {
    // 时区
    public static final ZoneId zoneId = ZoneId.systemDefault();
    public static final TimeZone timeZone = TimeZone.getTimeZone(zoneId);

    // UTC时间格式表示为‌YYYYMMDDTHHMMSSZ 其中Z表示协调世界时
    public static final ZoneId zoneUtc = ZoneId.of("UTC");
    public static final TimeZone timeUtc = TimeZone.getTimeZone(zoneUtc);


    public static final String DEFAULT_DAY = "2020-01-01 ";
    public static final String TIME_BEGIN = " 00:00:00";
    public static final String TIME_END = " 23:59:59";

    // 默认日期格式---禁止其他类型---这是标准
    public static final String YYYY = "yyyy";
    public static final String YYYY_MM = "yyyy-MM";
    public static final String YYYY_MM_DD = "yyyy-MM-dd";
    public static final String HH_MM_SS = "HH:mm:ss";
    public static final String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
    public static final String YYYY_MM_DD_HH_MM_SSSSS = "yyyy-MM-dd HH:mm:ss.SSS";
    public static final String YYYY_MM_DDTHH_MM_SSSSSZ = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";//就是要这么写 中间有个单分号,不写的化会报错

    //DateTimeFormatter线程安全
    private final static DateTimeFormatter dtfYear = DateTimeFormatter.ofPattern(YYYY);
    private final static DateTimeFormatter dtfYearMonth = DateTimeFormatter.ofPattern(YYYY_MM);
    private final static DateTimeFormatter dtfDate = DateTimeFormatter.ofPattern(YYYY_MM_DD);
    private final static DateTimeFormatter dtfTime = DateTimeFormatter.ofPattern(HH_MM_SS);
    private final static DateTimeFormatter dtfDateTime = DateTimeFormatter.ofPattern(YYYY_MM_DD_HH_MM_SS);
    private final static DateTimeFormatter dtfDateTimeSss = DateTimeFormatter.ofPattern(YYYY_MM_DD_HH_MM_SSSSS);
    private final static DateTimeFormatter dtfUtcDateTimeSssZ = DateTimeFormatter.ofPattern(YYYY_MM_DDTHH_MM_SSSSSZ);//utc时间
    private final static Map<String, DateTimeFormatter> dtfMap = new HashMap<>();

    static {
        dtfMap.put(YYYY, dtfYear);
        dtfMap.put(YYYY_MM, dtfYearMonth);
        dtfMap.put(YYYY_MM_DD, dtfDate);
        dtfMap.put(HH_MM_SS, dtfTime);
        dtfMap.put(YYYY_MM_DD_HH_MM_SS, dtfDateTime);
        dtfMap.put(YYYY_MM_DD_HH_MM_SSSSS, dtfDateTimeSss);
        dtfMap.put(YYYY_MM_DDTHH_MM_SSSSSZ, dtfUtcDateTimeSssZ);
    }

    //date转str
    public static String getDate() {
        return nowDate().format(dtfMap.get(YYYY_MM_DD));
    }

    public static String getTime() {
        return nowDateTime().format(dtfMap.get(HH_MM_SS));
    }

    public static String getDateTime() {
        return nowDateTime().format(dtfMap.get(YYYY_MM_DD_HH_MM_SS));
    }

    public static String getDate(Date date) {
        return dateToStr(date, YYYY_MM_DD);
    }

    public static String getTime(Date date) {
        return dateToStr(date, HH_MM_SS);
    }

    public static String getDateTime(Date date) {
        return dateToStr(date, YYYY_MM_DD_HH_MM_SS);
    }

    public static String dateToStr(Date date, String format) {
        return date2LocalDateTime(date).format(dtfMap.get(format));
    }

    public static Date strToDate(String str, String format) {
        String tmpStr = null;
        DateTimeFormatter dtf = null;
        switch (format) {
            case YYYY_MM_DD_HH_MM_SSSSS:
                tmpStr = str;
                dtf = dtfMap.get(format);
                break;

            case YYYY_MM_DD_HH_MM_SS:
                tmpStr = str;
                dtf = dtfMap.get(format);
                break;

            case YYYY_MM_DD:
                tmpStr = str + TIME_BEGIN;
                dtf = dtfMap.get(YYYY_MM_DD_HH_MM_SS);//追加了长度,改变类型
                break;

            case HH_MM_SS:
                tmpStr = DEFAULT_DAY + str;
                dtf = dtfMap.get(YYYY_MM_DD_HH_MM_SS);//追加了长度,改变类型
                break;

            case YYYY_MM:
                tmpStr = str + "-01" + TIME_BEGIN;
                dtf = dtfMap.get(YYYY_MM_DD_HH_MM_SS);//追加了长度,改变类型
                break;
            case YYYY:
                tmpStr = str + "-01-01" + TIME_BEGIN;
                dtf = dtfMap.get(YYYY_MM_DD_HH_MM_SS);//追加了长度,改变类型
                break;
            default:
                return null;
        }

        //先格式转换
        return localDateTime2Date(LocalDateTime.parse(tmpStr, dtf));
    }

    public static String getDateTimeBegin() {
        String day = nowDateTime().format(dtfMap.get(YYYY_MM_DD));
        return day + TIME_BEGIN;
    }

    public static String getDateTimeEnd() {
        String day = nowDateTime().format(dtfMap.get(YYYY_MM_DD));
        return day + TIME_END;
    }

    public static Date getDayBegin() {
        return getDayBegin(null);
    }

    /**
     * 获取当天的开始时间
     *
     * @return
     */
    public static Date getDayBegin(Date date) {
        Calendar cal = getCalendar();
        if (date != null) {
            cal.setTime(date);//先初始化时间,后面再覆盖
        }

        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);
        return cal.getTime();
    }

    /**
     * 获取当天的结束时间
     *
     * @return
     */
    public static Date getDayEnd() {
        return getDayEnd(null);
    }

    public static Date getDayEnd(Date date) {
        Calendar cal = getCalendar();
        if (date != null) {
            cal.setTime(date);//先初始化时间,后面再覆盖
        }
        cal.set(Calendar.HOUR_OF_DAY, 23);
        cal.set(Calendar.MINUTE, 59);
        cal.set(Calendar.SECOND, 59);
        return cal.getTime();
    }

    public static Date addHour(int addHour) {
        return calcDate(new Date(), null, null, null, addHour, null, null);
    }

    public static Date addHour(Date date, int addHour) {
        return calcDate(date, null, null, null, addHour, null, null);
    }

    public static Date addDay(int addDay) {
        return calcDate(new Date(), null, null, addDay, null, null, null);
    }

    public static Date addDay(Date date, int addDay) {
        return calcDate(date, null, null, addDay, null, null, null);
    }

    public static String getDate(int addDay) {
        return getDate(new Date(), addDay);
    }

    public static String getDate(Date date, int addDay) {
        Date tmpDate = addDay(date, addDay);
        return date2LocalDateTime(tmpDate).format(dtfMap.get(YYYY_MM_DD));
    }

    public static Date getMonthBegin() {
        return getMonthBegin(new Date());
    }

    public static Date addMonth(Date date, int addMonth) {
        Calendar cal = getCalendar();
        cal.setTime(date);
        cal.add(Calendar.MONTH, addMonth);//加一个月
        return cal.getTime();
    }

    public static Date addMinute(Date date, int addMinute) {
        Calendar cal = getCalendar();
        cal.setTime(date);
        cal.add(Calendar.MINUTE, addMinute);
        return cal.getTime();
    }

    //这个月份的第一天
    public static Date getMonthBegin(Date date) {
        Calendar cal = getCalendar();
        cal.setTime(date);
        cal.set(Calendar.DAY_OF_MONTH, 1);//第一天
        cal.set(Calendar.HOUR_OF_DAY, 0);//0时
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);
        return cal.getTime();
    }

    public static Date getMonthEnd() {
        return getMonthEnd(new Date());
    }

    //这个月份的最后一天
    public static Date getMonthEnd(Date date) {
        Calendar cal = getCalendar();
        cal.setTime(date);
        int curMonth = cal.get(Calendar.MONTH);
        cal.set(Calendar.MONTH, curMonth + 1);//月份
        cal.set(Calendar.DAY_OF_MONTH, 1);//第一天
        cal.set(Calendar.DAY_OF_MONTH, -1);//减少一天
        cal.set(Calendar.HOUR_OF_DAY, 23);
        cal.set(Calendar.MINUTE, 59);
        cal.set(Calendar.SECOND, 59);
        cal.set(Calendar.MILLISECOND, 0);
        return cal.getTime();
    }

    public static String getDate(String strDate, int addDay) {
        LocalDateTime parse = LocalDateTime.parse(strDate, dtfMap.get(YYYY_MM_DD));
        Date date = localDateTime2Date(parse);

        return getDate(date, addDay);
    }

    public static String getDateTime(int addDay) {
        return getDateTime(new Date(), addDay);
    }

    public static String getDateTime(Date date, int addDay) {
        Date tmpDate = calcDate(date, null, null, addDay, null, null, null);
        return date2LocalDateTime(tmpDate).format(dtfMap.get(YYYY_MM_DD_HH_MM_SS));
    }

    public static String getDateTimeBegin(int addDay) {
        return getDateTimeBegin(new Date(), addDay);
    }

    public static String getDateTimeBegin(Date date, int addDay) {
        Date tmpDate = calcDate(date, null, null, addDay, null, null, null);
        return date2LocalDateTime(tmpDate).format(dtfMap.get(YYYY_MM_DD)) + TIME_BEGIN;
    }

    public static String getDateTimeEnd(int addDay) {
        return getDateTimeEnd(new Date(), addDay);
    }

    public static String getDateTimeEnd(Date date, int addDay) {
        Date tmpDate = calcDate(date, null, null, addDay, null, null, null);
        return date2LocalDateTime(tmpDate).format(dtfMap.get(YYYY_MM_DD)) + TIME_END;
    }

    public static String toUtcDateTime(Date date) {
        return date2UtcLocalDateTime(date).format(dtfMap.get(YYYY_MM_DDTHH_MM_SSSSSZ));
    }

    public static String toUtcDateTime(Date date, int addDay) {
        Date tmpDate = calcDate(date, null, null, addDay, null, null, null);
        return date2UtcLocalDateTime(tmpDate).format(dtfMap.get(YYYY_MM_DDTHH_MM_SSSSSZ));
    }

    public static String toUtcDateTimeBegin(Date date, int addDay) {
        String dateTimeBegin = getDateTimeBegin(date, addDay);
        Date tmpDate = strToDate(dateTimeBegin, YYYY_MM_DD_HH_MM_SS);
        return date2UtcLocalDateTime(tmpDate).format(dtfMap.get(YYYY_MM_DDTHH_MM_SSSSSZ));
    }

    public static String toUtcDateTimeEnd(Date date, int addDay) {
        String dateTimeEnd = getDateTimeEnd(date, addDay);
        Date tmpDate = strToDate(dateTimeEnd, YYYY_MM_DD_HH_MM_SS);
        return date2UtcLocalDateTime(tmpDate).format(dtfMap.get(YYYY_MM_DDTHH_MM_SSSSSZ));
    }

    // 今天是当前月的第几天
    public static Integer dayOfMonth(Date date) {
        return calcDateDiffer(date, Calendar.DAY_OF_MONTH);
    }

    // 今天是当前年的第几天
    public static Integer dayOfYear(Date date) {
        return calcDateDiffer(date, Calendar.DAY_OF_YEAR);
    }

    //day差值
    public static Integer differDay(Date date1, Date date2) {
        Long diff = date1.getTime() - date2.getTime();
        Long day = diff / (1000 * 3600 * 24);
        return day.intValue();
    }

    //年份差
    public static Integer differYear(Date date1, Date date2) {
        return calcDateDiffer(date1, date2, Calendar.YEAR);
    }

    //月份差
    public static Integer differMonth(Date date1, Date date2) {
        Integer year = calcDateDiffer(date1, date2, Calendar.YEAR);
        Integer month = calcDateDiffer(date1, date2, Calendar.MONTH);
        return year * 12 + month;
    }

    //计算时间差
    private static Integer calcDateDiffer(Date date1, Date date2, int model) {
        if (date1 == null || date2 == null) {
            return null;
        }

        Integer res = null;
        Calendar cal1 = getCalendar();
        cal1.setTime(date1);
        Calendar cal2 = getCalendar();
        cal2.setTime(date2);

        if (model == Calendar.DAY_OF_YEAR) {
            res = cal1.get(model) - cal2.get(model);
        } else if (model == Calendar.HOUR) {
            res = cal1.get(model) - cal2.get(model);
        } else if (model == Calendar.YEAR) {
            res = cal1.get(model) - cal2.get(model);
        } else if (model == Calendar.MONTH) {
            res = cal1.get(model) - cal2.get(model);
        } else if (model == Calendar.WEEK_OF_MONTH) {
            res = cal1.get(model) - cal2.get(model);
        } else if (model == Calendar.DAY_OF_MONTH) {
            res = cal1.get(model) - cal2.get(model);
        }
        return res;
    }

    //计算时间差
    private static Integer calcDateDiffer(Date date, int model) {
        if (date == null) {
            return null;
        }

        Integer res = null;
        Calendar cal = getCalendar();
        cal.setTime(date);
        res = cal.get(model);

        if (model == Calendar.DAY_OF_YEAR) {
            res = cal.get(model);
        } else if (model == Calendar.HOUR) {
            res = cal.get(model);
        } else if (model == Calendar.YEAR) {
            res = cal.get(model);
        } else if (model == Calendar.MONTH) {
            res = cal.get(model);
        } else if (model == Calendar.WEEK_OF_MONTH) {
            res = cal.get(model);
        } else if (model == Calendar.DAY_OF_MONTH) {
            res = cal.get(model);
        }
        return res;
    }

    //对date进行计算
    private static Date calcDate(Date date, Integer addYear, Integer addMonth, Integer addDay, Integer addHour, Integer addMinute, Integer addSecond) {
        Calendar cal = getCalendar();
        cal.setTime(date);
        if (addYear != null) {
            cal.add(Calendar.YEAR, addYear);
        }
        if (addMonth != null) {
            cal.add(Calendar.MONTH, addMonth);
        }
        if (addDay != null) {
            cal.add(Calendar.DAY_OF_YEAR, addDay);
        }

        if (addHour != null) {
            cal.add(Calendar.HOUR_OF_DAY, addHour);
        }

        if (addMinute != null) {
            cal.add(Calendar.MINUTE, addMinute);
        }

        if (addSecond != null) {
            cal.add(Calendar.SECOND, addSecond);
        }
        return cal.getTime();
    }

    private static Calendar getCalendar() {
        Calendar cal = Calendar.getInstance();
        cal.setTimeZone(TimeZone.getTimeZone(zoneId));
        return cal;
    }

    private static LocalDate nowDate() {
        return LocalDate.now(zoneId);
    }

    private static LocalDateTime nowDateTime() {
        return LocalDateTime.now(zoneId);
    }

    private static Date localDateTime2Date(LocalDateTime localDateTime) {
        return Date.from(localDateTime.atZone(zoneId).toInstant());
    }

    private static LocalDateTime date2LocalDateTime(Date date) {
        return date.toInstant().atZone(zoneId).toLocalDateTime();
    }

    private static Date utcLocalDateTime2Date(LocalDateTime localDateTime) {
        return Date.from(localDateTime.atZone(zoneUtc).toInstant());
    }

    private static LocalDateTime date2UtcLocalDateTime(Date date) {
        return date.toInstant().atZone(zoneUtc).toLocalDateTime();
    }

    public static boolean isWorkDay(Date date) {
        Calendar c = getCalendar();
        c.setTime(date);
        int weekday = c.get(Calendar.DAY_OF_WEEK);
        if (weekday == 1 || weekday == 7) {
            return false;
        } else {
            return true;
        }
    }

    public static void main(String[] args) {
        String time = getTime();
        Date date = new Date();
        String toUTCDateTime = toUtcDateTime(date, 0);
        System.out.println(getDayEnd());
        System.out.println(getDayBegin(new Date()));
        System.out.println(time);
        System.out.println(strToDate("2022", YYYY));
        System.out.println(strToDate("2022-02", YYYY_MM));
        System.out.println(strToDate("11:11:11", HH_MM_SS));
        System.out.println(strToDate("2022-02-02", YYYY_MM_DD));
        System.out.println(strToDate("2022-02-02 11:11:11", YYYY_MM_DD_HH_MM_SS));
        System.out.println("toUTCDateTime " + toUTCDateTime);
    }
}
